package com.derbysoft.ota.application.translator.rq;

import java.util.List;
import java.util.Map.Entry;

import com.derbysoft.modules.eip.translator.Translator;
import com.derbysoft.ota.application.translator.MetaDatas;
import com.derbysoft.ota.application.util.Dates;
import com.derbysoft.ota.domain.city.CityCodeService;
import com.derbysoft.ota.interfaces.protocol._2009a.DateTimeSpanType;
import com.derbysoft.ota.interfaces.protocol._2009a.GuestCountType;
import com.derbysoft.ota.interfaces.protocol._2009a.HotelSearchCriteriaType;
import com.derbysoft.ota.interfaces.protocol._2009a.HotelSearchCriterionType;
import com.derbysoft.ota.interfaces.protocol._2009a.ItemSearchCriterionType;
import com.derbysoft.ota.interfaces.protocol._2009a.RoomStayCandidateType;
import com.derbysoft.ota.interfaces.protocol._2009a.GuestCountType.GuestCount;
import com.derbysoft.ota.interfaces.protocol._2009a.HotelSearchCriteriaType.Criterion;
import com.derbysoft.ota.interfaces.protocol._2009a.ItemSearchCriterionType.HotelRef;
import com.derbysoft.remote.dswitch.generic.constants.SmokingType;
import com.derbysoft.remote.dswitch.generic.dto.PositionDTO;
import com.derbysoft.remote.dswitch.hotel.dto.GuestCountDTO;
import com.derbysoft.remote.dswitch.hotel.dto.HotelSearchCriteriaDTO;
import com.derbysoft.remote.dswitch.hotel.dto.MultiPropertiesCriteria;
import com.derbysoft.remote.dswitch.hotel.dto.RoomStayCandidateDTO;
import com.derbysoft.remote.dswitch.hotel.dto.StayDateRangeDTO;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.googlecode.functionalcollections.FunctionalIterables;

public class HotelSearchCriteriaTranslator
        implements Translator<Entry<String, Criterion>, HotelSearchCriteriaDTO> {

    private LanguageTypeTranslator languageTypeTranslator = new LanguageTypeTranslator();
    private AgeQualifyingTypeTranslator ageQualifyingTypeTranslator = new AgeQualifyingTypeTranslator();

	private CityCodeService cityCodeService;

	@Inject
    public HotelSearchCriteriaTranslator(CityCodeService cityCodeService) {
        this.cityCodeService = cityCodeService;
    }

    @Override
    public HotelSearchCriteriaDTO translate(Entry<String, Criterion> source) {
        HotelSearchCriteriaDTO hotelSearchCriteriaDTO = new HotelSearchCriteriaDTO();
        hotelSearchCriteriaDTO.setLanguageType(languageTypeTranslator.translate(source.getKey()));
        Criterion criterion = source.getValue();
		hotelSearchCriteriaDTO.setStayDateRange(stayDateRange(criterion .getStayDateRange()));
        hotelSearchCriteriaDTO.setRoomStayCandidate(roomStayCandidate(criterion.getRoomStayCandidates()));
        hotelSearchCriteriaDTO.setMultiPropertiesCriteria(multiPropertiesCriteria(criterion));
        return hotelSearchCriteriaDTO;
    }

    private MultiPropertiesCriteria multiPropertiesCriteria(HotelSearchCriteriaType.Criterion criterion) {
        MultiPropertiesCriteria propertiesCriteria = new MultiPropertiesCriteria();
        propertiesCriteria.setPosition(position(criterion.getPosition()));
        HotelRef hotelRef = onlyHotelRef(criterion);
        propertiesCriteria.setCityCode(cityCode(hotelRef.getHotelCityCode()));
        propertiesCriteria.setHotelName(hotelRef.getHotelName());
        propertiesCriteria.setChainCodes(chainCodes(hotelRef));
        return propertiesCriteria;
    }

    private String cityCode(String iataCityCode) {
    	return cityCodeService.iata2switch(iataCityCode);
    }

	private HotelRef onlyHotelRef(Criterion criterion) {
    	return FunctionalIterables.make(criterion.getHotelRef()).getOnlyElement();
    }

	private List<String> chainCodes(HotelRef hotelRef) {
       String chainCode = hotelRef.getChainCode();
       if (chainCode == null) {
    	   return Lists.newArrayList();
       }
       return FunctionalIterables.make(chainCode.split(MetaDatas.COMMA)).map(new Function<String, String>() {
			public String apply(String from) {
				return from.trim();
			}
       }).toList();
    }

    private PositionDTO position(ItemSearchCriterionType.Position position) {
        if (position == null) {
            return null;
        }
        PositionDTO positionDTO = new PositionDTO();
        positionDTO.setLatitude(Float.valueOf(position.getLatitude()));
        positionDTO.setLongitude(Float.valueOf(position.getLongitude()));
        return positionDTO;
    }

    private RoomStayCandidateDTO roomStayCandidate(HotelSearchCriterionType.RoomStayCandidates roomStayCandidates) {
        if (roomStayCandidates == null) {
            return null;
        }
        RoomStayCandidateType roomStayCandidateType = roomStayCandidates.getRoomStayCandidate().iterator().next();
        if (roomStayCandidateType == null) {
            return null;
        }
        RoomStayCandidateDTO dto = new RoomStayCandidateDTO();
        dto.setNumberOfUnits(roomStayCandidateType.getQuantity());
        dto.setSmoking(smokingType(roomStayCandidateType.isNonSmoking()));
        dto.setGuestCounts(guestCounts(Preconditions.checkNotNull(roomStayCandidateType.getGuestCounts(), "GuestCounts required.")));
        return dto;
    }

    private List<GuestCountDTO> guestCounts(GuestCountType guestCounts) {
	    	if (!guestCounts.isIsPerRoom()) {
	    		throw new UnsupportedOperationException("Sorry, we haven't supported nonePerRoom case this moment.");
	    	}
    	
	    	return FunctionalIterables.make(guestCounts.getGuestCount()).map(new Function<GuestCount, GuestCountDTO>() {
			public GuestCountDTO apply(GuestCount from) {
				return guestCountDTO(from);
			}
		}).toList();
    }

    private GuestCountDTO guestCountDTO(GuestCountType.GuestCount count) {
        GuestCountDTO guestCountDTO = new GuestCountDTO();
        guestCountDTO.setAgeQualifyingType(ageQualifyingTypeTranslator.translate(count.getAgeQualifyingCode()));
        guestCountDTO.setCount(count.getCount());
        return guestCountDTO;
    }

    private SmokingType smokingType(Boolean nonSmoking) {
        if (nonSmoking != null && nonSmoking) {
            return SmokingType.NONSMOKING;
        } else {
            return SmokingType.INDIFFERENT;
        }
    }

    private StayDateRangeDTO stayDateRange(DateTimeSpanType dateTimeSpanType) {
	    	if (dateTimeSpanType == null) {
	    		return null;
	    	}
        StayDateRangeDTO stayDateRangeDTO = new StayDateRangeDTO();
        stayDateRangeDTO.setCheckInDate(Dates.parse(dateTimeSpanType.getStart()));
        stayDateRangeDTO.setCheckOutDate(Dates.parse(dateTimeSpanType.getEnd()));
        return stayDateRangeDTO;
    }
}
